package kit.docker.log;

import com.docker.core.mvc.Remark;
import com.jfinal.kit.Okv;
import kit.docker.exception.BaseException;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author wangbei
 * @date 2020/12/8 - 10:53
 */
@Remark("日志文件管理工具类")
public class LogFileKit {

    public static void main(String[] args) {
        //测试读取文件指定行数的数据(从后往前读取)
        String filePath = "C:\\Users\\ldl_e\\Desktop\\new.log";

//        //测试读取指定起始和结束行数的数据
//        Okv stringList = readLines(filePath, 220071L, 1000L);
//        for (Object o : stringList.keySet()) {
//            System.out.println(o + "：" + stringList.get(o));
//        }

        LinkedHashMap<Long, String> longStringLinkedHashMap = readLastLines(filePath, 1000);
        for (Long aLong : longStringLinkedHashMap.keySet()) {
            System.out.println(aLong + "：" + longStringLinkedHashMap.get(aLong));
        }

    }

    /**
     * @Param: [filePath, startNum 开始行号, readLine 读取行数]
     * @return: Okv
     * @Author: ldl
     * @Create: 2020/12/17 017 17:20
     */
    @Remark("从第N行开始读取指定的行数")
    public static Okv readLines(String filePath, Long startNum, Long readLine) {
        return readLines(filePath, "UTF-8", startNum, readLine);
    }

    /**
     * @Param: [filePath, charset, startNum 开始行号, readLine 读取行数]
     * @return: Okv
     * @Author: ldl
     * @Create: 2020/12/17 017 17:20
     */
    @Remark("从第N行开始读取指定的行数")
    public static Okv readLines(String filePath, String charset, Long startNum, Long readLine) {
        if (readLine > 1000) {
            throw new BaseException("最大读取1000行");
        }
        if (startNum < 0 || readLine < 0) {
            throw new BaseException("读取行数输入错误");
        }
        File file = new File(filePath);
        //检查文件
        checkFile(file);
        Okv cond = Okv.create();
        //获取文件中行数
        if (startNum > getLineNum(file)) {
            return cond;
        }
        readLine = startNum + readLine - 1;
        try {
            FileReader fileReader = new FileReader(file);
            LineNumberReader lineNumberReader = new LineNumberReader(fileReader);
            String line;
            while ((line = lineNumberReader.readLine()) != null) {
                if (lineNumberReader.getLineNumber() < startNum) {
                    continue;
                }
                cond.set(lineNumberReader.getLineNumber(), new String(line.getBytes(charset), StandardCharsets.UTF_8));
                if (lineNumberReader.getLineNumber() == readLine) {
                    break;
                }
            }
            lineNumberReader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cond;
    }

    /**
     * @Param: [filePath, lines]
     * @return: TODO
     * @Author: ldl
     * @Create: 2020/12/17 017 17:35
     */
    @Remark("读取最后N行")
    public static LinkedHashMap<Long, String> readLastLines(String filePath, int lines) {
        return readLastLines(filePath, "ISO-8859-1", lines);
    }

    /**
     * @Param: [filePath, lines]
     * @return: TODO
     * @Author: ldl
     * @Create: 2020/12/17 017 17:35
     */
    @Remark("读取最后N行")
    public static LinkedHashMap<Long, String> readLastLines(String filePath, String charset, int lines) {
        if (lines > 1000) {
            throw new BaseException("最大读取1000行");
        }
        File file = new File(filePath);
        //检查文件
        checkFile(file);
        LinkedHashMap<Long, String> result = new LinkedHashMap<>();
        try {
            Long lineNum = getLineNum(file);
            RandomAccessFile rf = new RandomAccessFile(file, "r");
            // 每次读取的字节数要和系统换行符大小一致
            byte[] c = new byte[1];
            LinkedHashMap<Long, String> map = new LinkedHashMap<>();
            // 在获取到指定行数和读完文档之前,从文档末尾向前移动指针,遍历文档每一个字节
            for (long pointer = rf.length(), lineSeparatorNum = 0; pointer >= 0 && lineSeparatorNum < lines; ) {
                // 移动指针
                rf.seek(pointer--);
                int readLength = rf.read(c);
                if (readLength != -1 && c[0] == 10) {
                    lineSeparatorNum++;
                    String line = rf.readLine();
                    map.put(lineNum, line == null ? "" : new String(line.getBytes(charset), StandardCharsets.UTF_8));
                    lineNum--;
                }
                // 扫描完依然没有找到足够的行数,将指针归0
                if (pointer == -1 && lineSeparatorNum < lines) {
                    rf.seek(0);
                }
            }
            map.entrySet().stream()
                    .sorted(Map.Entry.comparingByKey())
                    .forEachOrdered(x -> result.put(x.getKey(), x.getValue()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取文件总行数
     *
     * @param file
     * @return
     */
    public static Long getLineNum(File file) {
        if (file.exists()) {
            try {
                FileReader fileReader = new FileReader(file);
                LineNumberReader lineNumberReader = new LineNumberReader(fileReader);
                lineNumberReader.skip(Long.MAX_VALUE);
                long lines = lineNumberReader.getLineNumber() + 1;
                fileReader.close();
                lineNumberReader.close();
                return lines;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return 0L;
    }

    /**
     * 检查文件
     */
    private static void checkFile(File file) {
        if (file == null) {
            throw new BaseException("文件不存在");
        }
        if (file.isDirectory() || !file.canRead()) {
            throw new BaseException("文件无法读取");
        }
        if (file.length() > 1610612736) {//1.5G
            throw new BaseException("文件超出1.5G");
        }
    }

}